package com.wuhao.shootclient.pusher.ws;

import com.blankj.utilcode.util.TimeUtils;
import com.wuhao.shootclient.base.BaseApplication;
import com.wuhao.shootclient.pusher.ws.netty.lis.OnWsStatusChangeListener;
import com.wuhao.shootclient.util.LoggerHelper;
import com.wuhao.shootclient.util.eventbus.AppEventBus;
import com.wuhao.shootclient.util.eventbus.EventBusCode;

import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

public class WsClient {
    //关闭WebSocket时需要发送的Code
    public final int CLOSE_ERROR_CODE = 1000, CLOSE_FAILURE_CODE = 1001, CLOSE_NORMAL_CODE = 1002;

    final String TAG = "WsClient";
    //WebSocket实例，可用来发送消息
    WebSocket webSocketClient = null;
    //单例实例
    private volatile static WsClient instance = null;
    //服务端的地址
    private String url;
    //WebSocket状态改变监听
    private OnWsStatusChangeListener wsStatusChangeListener;

    private WsClient(OnWsStatusChangeListener wsStatusChangeListener) {
        this.wsStatusChangeListener = wsStatusChangeListener;
    }

    /**
     * 获取单例模式
     *
     * @return
     */
    public static WsClient getInstance(OnWsStatusChangeListener msgListener) {
        if (instance == null) {
            synchronized (WsClient.class) {
                instance = new WsClient(msgListener);
            }
        }
        return instance;
    }

    /**
     * 初始化WebSocket Client端
     * 配置读、写、连接超时时间
     *
     * @param url WebSocket Server端的地址
     */
    public void init(String url) {
        this.url = url;
        LoggerHelper.e(TAG, "url is:" + url);
        OkHttpClient mClient = new OkHttpClient.Builder()
                .readTimeout(3, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(3, TimeUnit.SECONDS)//设置写的超时时间
                .connectTimeout(3, TimeUnit.SECONDS)//设置连接超时时间
                .pingInterval(5, TimeUnit.SECONDS)// 设置 PING 帧发送间隔(需要和服务端商议，通常建议最好设置一个小于 60s 的值。)
                .build();

        webSocketClient = mClient.newWebSocket(new Request.Builder().url(url).build(), listener);
    }


    /**
     * 发送心跳数据
     */
    private void sendPulse() {
        Disposable s = Observable.interval(0, 1, TimeUnit.SECONDS)
                .subscribe((l -> {
                    if (webSocketClient != null) {
                        webSocketClient.send("");
                    }
                }), (throwable -> {

                }));
    }

    /**
     * 关闭Socket连接
     */
    public void close(int code, String reason) {
        switch (code) {
            case CLOSE_ERROR_CODE:
                webSocketClient.close(code, "error");
                break;
            case CLOSE_FAILURE_CODE:
                webSocketClient.close(code, reason);
                break;
            case CLOSE_NORMAL_CODE:
                webSocketClient.close(code, reason);
                break;

        }
        webSocketClient = null;
    }


    public WebSocket getWebSocketClient() {
        return webSocketClient;
    }

    //接收到的条数
    int receiveNum = 1;
    //是否已经发送过OnMessage状态,避免重复刷新页面
    boolean haveSendOnMessage = false;
    WebSocketListener listener = new WebSocketListener() {
        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            // WebSocket 连接建立
            super.onOpen(webSocket, response);
            //重连次数重置为0
            reconnectNum = 0;
            LoggerHelper.e(TAG, "status----onOpen");
            //向页面更新Socket状态
            wsStatusChangeListener.receiveStatus(WsStatus.ON_OPEN);
            //向Server发送初始的第一条信息(现不需要)
            //webSocket.send(new Gson().toJson(new ServerMsg("msg" + reconnectNum, "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg")));
            //接收到的条数
            receiveNum++;
//            Disposable subscribe = Observable.interval(0, 1, TimeUnit.SECONDS)
//                    .subscribe(o -> {
//                        webSocket.send(ByteString.of((byte) 0x9));
//                    });
        }

        /**
         * 接收文字类型的消息
         * @param webSocket socket实例
         * @param text 文字消息
         */
        @Override
        public void onMessage(WebSocket webSocket, String text) {
            // 收到服务端发送来的 String 类型消息
            super.onMessage(webSocket, text);
//            LoggerHelper.e(TAG,text);
            //是否已经发送过OnMessage状态，避免重复刷新页面
            if (!haveSendOnMessage) {
                LoggerHelper.e(TAG, "status----onMessage:" + ((text.length() > 500) ? "receiveMsg but length more than 500" : text));
                //已经发送过OnMessage状态
                haveSendOnMessage = true;
                //向页面更新Socket状态
                wsStatusChangeListener.receiveStatus(WsStatus.ON_MESSAGE);
            }
            //通过EventBus发送数据
            AppEventBus.getInstance().post(EventBusCode.EVENT_TYPE, text);
            //通过回调传递数据
            wsStatusChangeListener.receiveMsg(text);
        }

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            // 收到新消息时回调
            super.onMessage(webSocket, bytes);
            if (bytes.getByte(0) == 0xA) {
                LoggerHelper.e(TAG, "onMessage byte:received pong");
            }
        }

        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {
            // 收到服务端发来的 CLOSE 帧消息，准备关闭连接
            super.onClosing(webSocket, code, reason);
            LoggerHelper.e(TAG, "status----onClosing");
            //向页面更新Socket状态
            wsStatusChangeListener.receiveStatus(WsStatus.ON_CLOSING);
            //已经发送过OnMessage状态
            haveSendOnMessage = false;
        }

        /**
         * 当两个对等体都表示将不再传输消息并且连接已成功释放时调用。不会再给这个听众打电话。
         * @param webSocket
         * @param code
         * @param reason
         */
        @Override
        public void onClosed(WebSocket webSocket, int code, String reason) {
            // WebSocket 连接关闭
            super.onClosed(webSocket, code, reason);
            if (code == CLOSE_ERROR_CODE || code == CLOSE_FAILURE_CODE || code == CLOSE_NORMAL_CODE) {
                LoggerHelper.e(TAG, "code is :" + code + "客户端发起的关闭");
            } else {
                //关闭Socket
                close(CLOSE_NORMAL_CODE, reason);
            }
            LoggerHelper.e(TAG, "status----onClosed");
            //向页面更新Socket状态
            wsStatusChangeListener.receiveStatus(WsStatus.ON_CLOSED);
            //已经发送过OnMessage状态
            haveSendOnMessage = false;
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
            // 出错了
            super.onFailure(webSocket, t, response);
            //关闭Socket
            /*if (webSocket != null) {
                webSocket.close(CLOSE_FAILURE_CODE, "msg is:onFailure");
                webSocket = null;
                //close(CLOSE_FAILURE_CODE,"msg is:"+t.getMessage());
            }*/
            LoggerHelper.e(TAG, "status----onFailure" + t.getMessage());
            //向页面更新Socket状态
            wsStatusChangeListener.receiveStatus(WsStatus.ON_FAILURE);
            //已经发送过OnMessage状态
            haveSendOnMessage = false;
            //销毁webSocket，准备进行重连
            webSocketClient = null;
            //进行重连
            reconnect();
        }
    };

    private Long lastReconnectTime = 0L;//上一次断线重连的时间
    private int reconnectNum = 0;//断线重连尝试次数，不能进行无限制的断线重连
    private final Long RECONNECT_TIME_SPAN = 1000L;//重连间隔时间5S

    /**
     * 断线重连机制
     */
    private void reconnect() {
        //获取上一次断线重连的时间间隔
        long timeSpan = TimeUtils.getNowMills() - lastReconnectTime;
        //5秒内不再重连，重连次数不能超过10次
//        timeSpan > RECONNECT_TIME_SPAN &&
        if (webSocketClient == null && reconnectNum < 10) {
            //开启子线程，3秒后进行重连
            Disposable subscribe = Observable.timer(3, TimeUnit.SECONDS)
                    .subscribe((t) -> {
                        LoggerHelper.e(TAG, "status----reconnect");
                        wsStatusChangeListener.receiveStatus(WsStatus.ON_RECONNECT);
                        BaseApplication.canSendHeartBeat = true;
                        //记录当前重连的时间
                        lastReconnectTime = TimeUtils.getNowMills();
                        //进行重连
                        webSocketClient = new OkHttpClient().newWebSocket(new Request.Builder().url(url).build(), listener);
                    }, (throwable) -> {
                        LoggerHelper.e(TAG, throwable.getMessage());
                    });
        }

    }
}
